#include "testGameOfLifeTimer.h"

testGameOfLifeTimer::testGameOfLifeTimer(void)
{
	// initialize the template objects
	cellSourceTemplate = vtkSphereSource::New();
	cellSourceTemplate->SetRadius(1.0);
	cellSourceTemplate->SetPhiResolution(5);
	cellSourceTemplate->SetThetaResolution(5);

	// initialize the universe
	currentStep = 0;
}

testGameOfLifeTimer::~testGameOfLifeTimer(void)
{
}

testGameOfLifeTimer* testGameOfLifeTimer::New(){
	testGameOfLifeTimer *cb = new testGameOfLifeTimer();
	return cb;
}

void testGameOfLifeTimer::initialize(vtkRenderer *ren,vtkRenderWindowInteractor *rwint)
{
	renderer=ren;
	rwInteractor=rwint;
	randomize();
	count(0);
}

void testGameOfLifeTimer::setTimerToWatch(int timerId){
	timerToWatch = timerId;
}

int testGameOfLifeTimer::getTimerToWatch(){
	return timerToWatch;
}

void testGameOfLifeTimer::setRenderWindowInteractor(vtkRenderWindowInteractor *rwi)
{
	rwInteractor = rwi;
}

vtkRenderWindowInteractor* testGameOfLifeTimer::getRenderWindowInteractor()
{
	return rwInteractor;
}

void testGameOfLifeTimer::setRenderer(vtkRenderer *ren)
{
	renderer = ren;
}

vtkRenderer* testGameOfLifeTimer::getRenderer()
{
	return renderer;
}

void testGameOfLifeTimer::Execute(vtkObject *caller, unsigned long eventId, void *callData)
{
	if (vtkCommand::TimerEvent == eventId){
		int timerId = * static_cast<int *>(callData);
		if(timerId != this->timerToWatch) return;

		// calculate grid
		step();
	}
}

void testGameOfLifeTimer::randomize()
{
	vtkAppendPolyData *data = vtkAppendPolyData::New();

	srand(time(NULL));
	for(short i = 1; i <= SIZE; i++)
	{
		for(short j = 1; j <= SIZE; j++)
		{
			for(short k = 1; k <= SIZE; k++)
			{
				bool isAlive = rand()%2;
				universe[0][i][j][k].alive = isAlive;
				universe[0][i][j][k].age = 0;
				universe[1][i][j][k].alive = isAlive;
				universe[1][i][j][k].age = 0;
				
				vtkTransform *transform = vtkTransform::New();
				transform->Translate((2*i)-SIZE-1,(2*j)-SIZE-1,(2*k)-SIZE-1);
				vtkTransformFilter *filter = vtkTransformFilter::New();
				filter->SetTransform(transform);
				filter->SetInput(cellSourceTemplate->GetOutput());
				filter->Update();
				vtkPolyData *polyData = vtkPolyData::New();
				polyData->ShallowCopy(filter->GetOutput());
				cellMap[i-1][j-1][k-1] = polyData;

				if(isAlive) addCell(data,i,j,k);
			}
		}
	}

	cellMapperTemplate = vtkPolyDataMapper::New();
	cellMapperTemplate->SetInputConnection(data->GetOutputPort());
	cellActorTemplate = vtkActor::New();
	cellActorTemplate->SetMapper(cellMapperTemplate);
	renderer->AddActor(cellActorTemplate);
}

// can't really output a 3D matrix in a console
// so I'll just count living cells and output that
void testGameOfLifeTimer::count(int whichStep) //n is 
{
	int count = 0;
	for(short i = 1; i <= SIZE; i++)
		for(short j = 1; j <= SIZE; j++)
			for(short k = 1; k <= SIZE; k++)
				if(universe[whichStep][i][j][k].alive)
					count++;
	printf("\n%hu: %d", whichStep, count);
}

// apply the rules to the universe
// n=(1 or 0) denotes which universe is 'read' from
// the other is written to
// alternating between them each step saves an expensive memcpy call
void testGameOfLifeTimer::step()
{
	short nextStep, neighborcount;

	if(currentStep==0) nextStep = 1;
	else nextStep = 0;

	vtkAppendPolyData *data = vtkAppendPolyData::New();

	for(short i = 1; i <= SIZE; i++)
	{
		for(short j = 1; j <= SIZE; j++)
		{
			for(short k = 1; k <= SIZE; k++)
			{
				neighborcount = 0;
				if(universe[currentStep][i][j][k-1].alive)
					neighborcount++;
				if(universe[currentStep][i][j][k+1].alive)
					neighborcount++;
				if(universe[currentStep][i][j-1][k].alive)
					neighborcount++;
				if(universe[currentStep][i][j-1][k-1].alive)
					neighborcount++;
				if(universe[currentStep][i][j-1][k+1].alive)
					neighborcount++;
				if(universe[currentStep][i][j+1][k].alive)
					neighborcount++;
				if(universe[currentStep][i][j+1][k-1].alive)
					neighborcount++;
				if(universe[currentStep][i][j+1][k+1].alive)
					neighborcount++;
				if(universe[currentStep][i-1][j][k].alive)
					neighborcount++;
				if(universe[currentStep][i-1][j][k-1].alive)
					neighborcount++;
				if(universe[currentStep][i-1][j][k+1].alive)
					neighborcount++;
				if(universe[currentStep][i-1][j-1][k].alive)
					neighborcount++;
				if(universe[currentStep][i-1][j-1][k-1].alive)
					neighborcount++;
				if(universe[currentStep][i-1][j-1][k+1].alive)
					neighborcount++;
				if(universe[currentStep][i-1][j+1][k].alive)
					neighborcount++;
				if(universe[currentStep][i-1][j+1][k-1].alive)
					neighborcount++;
				if(universe[currentStep][i-1][j+1][k+1].alive)
					neighborcount++;
				if(universe[currentStep][i+1][j][k].alive)
					neighborcount++;
				if(universe[currentStep][i+1][j][k-1].alive)
					neighborcount++;
				if(universe[currentStep][i+1][j][k+1].alive)
					neighborcount++;
				if(universe[currentStep][i+1][j-1][k].alive)
					neighborcount++;
				if(universe[currentStep][i+1][j-1][k-1].alive)
					neighborcount++;
				if(universe[currentStep][i+1][j-1][k+1].alive)
					neighborcount++;
				if(universe[currentStep][i+1][j+1][k].alive)
					neighborcount++;
				if(universe[currentStep][i+1][j+1][k-1].alive)
					neighborcount++;
				if(universe[currentStep][i+1][j+1][k+1].alive)
					neighborcount++;

				if(!universe[currentStep][i][j][k].alive)
				{
					//todo: neighborcount>4?
					if(neighborcount > 4)
					{
						universe[nextStep][i][j][k].alive = true;
						universe[nextStep][i][j][k].age = 0;
						addCell(data,i,j,k);
					}
					else
					{
						universe[nextStep][i][j][k].alive = false;
					}
				}
				else
				{
					//todo: 3 < neighborcount < 7 ?
					//code this to short circuit quickest
					if((neighborcount >= 4) && (neighborcount <= 6))
					{
						universe[nextStep][i][j][k].age++;
						addCell(data,i,j,k);
					}
					else
					{
						universe[nextStep][i][j][k].alive = false;
					}
				}
			}
		}
	}

	cellMapperTemplate->SetInputConnection(data->GetOutputPort());
	rwInteractor->Render();

	count(nextStep);
	if(currentStep==0)
	{
		currentStep=1;
		nextStep=0;
	}
	else
	{
		currentStep=0;
		nextStep=1;
	}
}
void testGameOfLifeTimer::destroyCellActor(int i, int j, int k)
{
	renderer->RemoveActor(universeActors[i][j][k]);
	universeActors[i][j][k]->Delete();
}

void testGameOfLifeTimer::createCellActor(int i, int j, int k)
{
	universeActors[i][j][k] = vtkActor::New();
	universeActors[i][j][k]->ShallowCopy(cellActorTemplate);
	universeActors[i][j][k]->SetPosition((2*i)+1-(SIZE)-2,(2*j)+1-SIZE-2,(2*k)+1-SIZE-2);
	renderer->AddActor(universeActors[i][j][k]);
}

void testGameOfLifeTimer::addCell(vtkAppendPolyData *collection, int i, int j, int k)
{
	//Translate((2*i)+1-(SIZE)-2,(2*j)+1-SIZE-2,(2*k)+1-SIZE-2);
	//vtkTransformFilter *filter = vtkTransformFilter::New();
	//filter->SetTransform(transform);
	//filter->SetInput(cellSourceTemplate->GetOutput());
	//filter->Update();

	//vtkPolyData *data = vtkPolyData::New();
	//data->ShallowCopy(transformMap[i-1][j-1][k-1]->GetOutput());
	collection->AddInput(cellMap[i-1][j-1][k-1]);
}
